package cn.net.dpi.lock.support;

import org.springframework.util.Assert;
import org.springframework.util.StopWatch;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class LockTrace {

    Long threadId;

    LockInfo lockInfo;

    LocalDateTime startTime;

    List<StopWatch> stages;

    LocalDateTime endTime;

    boolean acquired;

    public LockTrace(
            String lockId
            , Integer expire
            , Integer timeout
    ) {
        this.threadId = Thread.currentThread().getId();
        this.lockInfo = new LockInfo(lockId, expire, timeout);
        stages = new ArrayList<>();
    }

    public Long getThreadId() {
        return threadId;
    }

    public void setThreadId(Long threadId) {
        this.threadId = threadId;
    }

    public LockInfo getLockInfo() {
        return lockInfo;
    }

    public void setLockInfo(LockInfo lockInfo) {
        this.lockInfo = lockInfo;
    }

    public LocalDateTime getStartTime() {
        return startTime;
    }

    public void setStartTime(LocalDateTime startTime) {
        this.startTime = startTime;
    }

    public List<StopWatch> getStages() {
        return stages;
    }

    public void setStages(List<StopWatch> stages) {
        this.stages = stages;
    }

    public LocalDateTime getEndTime() {
        return endTime;
    }

    public void setEndTime(LocalDateTime endTime) {
        this.endTime = endTime;
    }

    public boolean isAcquired() {
        return acquired;
    }

    public void setAcquired(boolean acquired) {
        this.acquired = acquired;
    }

    public void start() {
        this.setStartTime(LocalDateTime.now());
    }

    public void addStage(StopWatch stopWatch) {
        Optional.ofNullable(stopWatch)
                .ifPresent(sw -> this.getStages().add(sw));
    }

    public void setLockValue(String lockValue) {
        this.getLockInfo()
                .setLockValue(lockValue);
    }

    public void end(boolean acquired) {
        this.setEndTime(LocalDateTime.now());
        this.setAcquired(acquired);
    }

    @Override
    public String toString() {

        StringBuilder stage = new StringBuilder();
        Optional.ofNullable(stages)
                .ifPresent(list -> list.stream()
                        .forEach(sw -> stage.append("   " + sw.prettyPrint() + "\n")));

        return  "\n==================================================================\n"
                + "线程 ID： " + threadId + "\n"
                + "锁信息： \n"
                + lockInfo + "\n"
                + "开始时间： " + DateTimeFormatter.ofPattern("yyyy-MM-dd").format(startTime) + "\n"
                + "锁经历： \n"
                + stage
                + "结束时间： " + DateTimeFormatter.ofPattern("yyyy-MM-dd").format(endTime) + "\n"
                + "锁结果： " + (acquired ? "成功" : "失败")  + "\n"
                + "==================================================================";
    }

    static class LockInfo {

        String lockId;
        String lockValue;
        Integer expire;
        Integer timeout;

        public LockInfo(String lockId, Integer expire, Integer timeout) {
            Assert.hasText(lockId, "lockId 不能为空");
            this.lockId = lockId;

            this.expire = expire;

            Assert.isTrue(timeout >= 0, "timeout 不能小于 0");
            this.timeout = timeout;
        }

        public String getLockId() {
            return lockId;
        }

        public void setLockId(String lockId) {
            this.lockId = lockId;
        }

        public String getLockValue() {
            return lockValue;
        }

        public void setLockValue(String lockValue) {
            this.lockValue = lockValue;
        }

        public Integer getExpire() {
            return expire;
        }

        public void setExpire(Integer expire) {
            this.expire = expire;
        }

        public Integer getTimeout() {
            return timeout;
        }

        public void setTimeout(Integer timeout) {
            this.timeout = timeout;
        }

        @Override
        public String toString() {
            return "    lockId： " + lockId + "\n"
                    + "     lockValue： " + lockValue + "\n"
                    + "     expire： " + expire + "s\n"
                    + "     timeout： " + timeout + "s\n"
                    ;
        }
    }
}
